package com.acargil.sudoku.model;

import java.util.LinkedList;
import java.util.List;

import com.acargil.sudoku.model.algorithm.possibility.StandardPossibilityManager;
import com.acargil.sudoku.model.exception.InvalidCellValueException;
import com.acargil.sudoku.model.exception.InvalidCoordinateException;
import com.acargil.sudoku.model.exception.SudokuValidationException;
import com.acargil.sudoku.model.structure.StandardGrid;


/**
 * @author Oguzhan
 */
public class StandardSudokuEngineImpl extends StandardSudokuEngine{

	private static final long serialVersionUID = 1L;
	
	private StandardGrid grid;
	private StandardPossibilityManager possibilityManager; 
	private List<StandardGrid.Memento> undoHistory;
	private List<StandardGrid.Memento> redoHistory;
	
	public StandardSudokuEngineImpl() {
		grid = new StandardGrid();
		possibilityManager = new StandardPossibilityManager(grid);
		undoHistory = new LinkedList<StandardGrid.Memento>();
		redoHistory = new LinkedList<StandardGrid.Memento>();
	}

	@Override
	public void setCellValue(int x, int y, int value) throws InvalidCoordinateException, InvalidCellValueException {
		StandardGrid.Memento memento = grid.saveToMemento();
		grid.setCell(value, x, y);
		undoHistory.add(memento);
		redoHistory.clear();
	}

	@Override
	public int getCellValue(int x, int y) throws InvalidCoordinateException {
		return grid.getCell(x, y);
	}

	@Override
	public void clearCellValue(int x, int y) throws InvalidCoordinateException{
		StandardGrid.Memento memento = grid.saveToMemento();
		grid.clearCellValue(x,y);
		undoHistory.add(memento);
		redoHistory.clear();
	}

	@Override
	public void setAllCellValues(int[][] values) throws InvalidCellValueException {
		StandardGrid.Memento memento = grid.saveToMemento();
		grid.setCells(values);
		undoHistory.add(memento);
		redoHistory.clear();
	}

	@Override
	public int[][] getAllCellValues() {
		return grid.getCells();
	}

	@Override
	public void clearAllCellValues() {
		StandardGrid.Memento memento = grid.saveToMemento();
		grid.clearAllCell();
		undoHistory.add(memento);
		redoHistory.clear();
	}

	@Override
	public String checkStateOfGame() {
		
		if (grid.getNumberOfFullCells() == StandardGrid.NUMBER_OF_CELLS) {
			try {
				grid.validate();
				return SudokuGameStatus.COMPLETED_SUCCESSFULLY.getCode();
			} catch (SudokuValidationException e) {
				return SudokuGameStatus.COMPLETED_UNSUCCESSFULLY.getCode();
			}
		}
		else if (grid.getNumberOfFullCells() == 0) {
			return SudokuGameStatus.EMPTY.getCode();
		} 
		else {
			return SudokuGameStatus.NOT_COMPLETED.getCode();
		}
		
	}

	@Override
	public void validate() throws SudokuValidationException {
		grid.validate();
	}
	
	@Override
	public void undoLastMove() {
		
		StandardGrid.Memento currentState, previousState;
		int size = undoHistory.size();
		
		if (size > 0) {	// if undoHistory has a MEMENTO
			// the currentState of the game is visible and contained in grid variable
			currentState = grid.saveToMemento();
			redoHistory.add(currentState);
			
			previousState = undoHistory.get(size-1);
			grid.restoreFromMemento(previousState);
			undoHistory.remove(size-1);
		}
	}

	@Override
	public void redoLastMove() {
		
		StandardGrid.Memento currentState, previousState;
		int size = redoHistory.size();
		
		if (redoHistory.size() > 0) { // if any MEMENTO exists on redoHistory  

			currentState = grid.saveToMemento();
			undoHistory.add(currentState);
			
			previousState = redoHistory.get(size-1);
			grid.restoreFromMemento(previousState);
			redoHistory.remove(size-1);
		}
	}

	@Override
	public int[][][] getPossibilitiesOfAllCells() {
		return possibilityManager.getPossibilitiesOfAllCells();
	}

	@Override
	public boolean isAnyCellFilled() {
		return grid.getNumberOfFullCells() > 0;
	}

}